﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Dycox.ComponentModel;

namespace Dycox.Workflows
{
    [DebuggerDisplay("{Name}")]
    public abstract class Stage : InitializableObject
    {
        internal const char FullNameSeparator = '.';

        internal Stage(string name, StageUsage usage)
        {
            if (name == null)
                throw new ArgumentNullException("name");

            _usage = usage;
            _name = name;
        }

        private Type _contractType;

        public Type ContractType
        {
            get { return _contractType; }
            set
            {
                ThrowIfNotInitializing();

                if (value != null)
                {
                    Type type = typeof(StageContract);
                    if (value.IsAbstract || type != value && !value.IsSubclassOf(type))
                        throw new ArgumentException("contractType is not valid.");
                }

                _contractType = value;
   
            }
        }

        public StageContract NewContract(OperationArguments args)
        {
            StageContract contract = CreateContractInternal();
            contract.InitializeNew(args, this);
            return contract;
        }

        public StageContract GetContract(WFStep step)
        {
            if (step == null)
                throw new ArgumentNullException("step");
            if (step.StageName != this.Name)
                throw new ArgumentException("Invalid step.");

            return step.GetUserState(this);
        }

        //[Obsolete]
        //public StageStateContract CreateContract(StateObject state)
        //{
        //    if (_contractType == null)
        //        throw new InvalidOperationException("Contract type not specified.");

        //    return CreateContractInternal(state);
        //}

        private StageContract CreateContractInternal()
        {
            if (_contractType == null)
                return null;

                return (StageContract)Activator.CreateInstance(_contractType);
        }

        private string _name;

        public string Name
        {
            get { return _name; }
        }

        private string _displayName;

        public string DisplayName
        {
            get { return _displayName ?? _name; }
            set
            {
                ThrowIfNotInitializing();
                _displayName = value;
            }
        }

        public string FullName
        {
            get
            {
                ValidateInWorkflow();
                return _workflow.Name + FullNameSeparator + _name;
            }
        }

        private StageProperties _properties;

        public StageProperties Properties
        {
            get
            {
                if (_properties == null)
                    _properties = new StageProperties();

                return _properties;
            }
        }

        private StageUsage _usage;

        public StageUsage Usage
        {
            get { return _usage; }
        }

        private StageClass _class;

        public StageClass Class
        {
            get { return _class; }
            set
            {
                ThrowIfNotInitializing();
                _class = value;
            }
        }

        private Workflow _workflow;

        public Workflow Workflow
        {
            get { return _workflow; }
            internal set { _workflow = value; }
        }

        //public TransitionRule Rule
        //{
        //    get
        //    {
        //        if (_workflow == null)
        //            throw new InvalidOperationException("Stage does not belong to any workflow.");

        //        return _workflow.Rules[this];
        //    }
        //}

        private void ValidateInWorkflow()
        {
            if (_workflow == null)
                throw new InvalidOperationException("Must attach this stage to a workflow first.");

        }

        public TransitionConditionCollection EnteringCondition
        {
            get
            {
                Transition t = _workflow.Transitions[_workflow.AnyStage, this];

                if (t != null)
                    return t.Conditions;
                else
                    return null;
            }
        }

        private string _prohibitMessage;

        public string ProhibitMessage
        {
            get { return _prohibitMessage; }
            set { _prohibitMessage = value; }
        }

        public Transition[] OutgoingTransitions
        {
            get
            {
                ValidateInWorkflow();
                return _workflow.Transitions.GetTransitionsInternal(this, null, TransitionTypes.Normal).ToArray();
            }
        }

        public Stage[] OutgoingStages
        {
            get
            {
                ValidateInWorkflow();
                return _workflow.Transitions.GetOutgoingStages(this);
            }
        }

        //public EnteringConditionCollection CreateEnteringConditionCollection(string errorMessage)
        //{
        //    if (_self == null)
        //    {
        //        _self = new Transition(this);
        //        _self.SourceStage = this;
        //    }

        //    if (_enteringConditions == null) _enteringConditions = new List<EnteringConditionCollection>(2);

        //    EnteringConditionCollection coll = new EnteringConditionCollection(_self);
        //    coll.ErrorMessage = errorMessage;
        //    _enteringConditions.Add(coll);
        //    return coll;
        //}


        //StageEventProcessorCollection[] _eventCollections;

        //private StageEventProcessorCollection GetEventCollection(EventScenario scenario, bool create)
        //{
        //    if (_eventCollections == null)
        //    {
        //        if (create) _eventCollections = new StageEventProcessorCollection[Enum.GetNames(typeof(EventScenario)).Length];
        //        else return null;
        //    }

        //    StageEventProcessorCollection coll;

        //    int index = ((int)scenario);
        //    if ((coll = _eventCollections[index]) == null)
        //    {
        //        if (create)
        //        {
        //            coll = new StageEventProcessorCollection(this, scenario);
        //            _eventCollections[index] = coll;
        //        }
        //    }

        //    return coll;
        //}

        //public StageEventProcessorCollection StepCreatedEvents
        //{
        //    get
        //    {
        //        return GetEventCollection(EventScenario.StepCreated, true);
        //    }
        //}

        //public StageEventProcessorCollection StepFinishedEvents
        //{
        //    get
        //    {
        //        return GetEventCollection(EventScenario.StepFinished, true);
        //    }
        //}

        protected override void BeginInit()
        {
        }

        protected override void EndInit()
        {
        }
    }

    public enum StageUsage
    {
        Normal,
        Entrance,
        Injection,
        PlaceHolder
    }

    public enum StageClass
    {
        Major,
        Minor
    }
}
